ko.bindingHandlers.tree = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = ko.utils.unwrapObservable(valueAccessor());

        var expandNode = function (node) {
            if ($(element).tree('getChildren', node.target).length == 0) {
                var path = getAbsPath(element, node);
                jsonrpc.invoke({
                    method: 'files',
                    params: {
                        path: path,
                        type: 0
                    },
                    success: function (data) {
                        if (data) {
                            $(element).tree('append', {
                                parent: node.target,
                                data: data.result
                            });
                            $(node.target).next().show();

                            selectNode(element, node);
                        }
                    }
                });
            } else {
                selectNode(element, node);
            }
        };

        var listFiles = function (node) {
            var path = getAbsPath(element, node);
            jsonrpc.invoke({
                method: 'files',
                params: {
                    path: path
                },
                success: function (data) {
                    viewModel.files([]);
                    if (data) {
                        var folderList = [];
                        var fileList = [];
                        var list = data.result;
                        for (var i in list) {
                            if (list[i].type == 0) {
                                folderList.push(list[i]);
                            } else {
                                fileList.push(list[i]);
                            }
                        }
                        viewModel.files(folderList.concat(fileList));
                    }
                    viewModel.path(path);
                }
            });
        };

        $(element).tree({
            animate: true,
            onBeforeExpand: function (node) {
                expandNode(node);
            },
            onSelect: function (node) {
                listFiles(node);
            },
            onDblClick: function (node) {
                $(element).tree('toggle', node.target);
            },
            formatter: function (node) {
                node.state = 'closed';
                return node.name;
            }
        });
        viewModel['tree']($(element));
    }
};

ko.bindingHandlers.icon = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var name = value.name;
        var src = '/images/explorer/' + (value.type == 0 ? 'folder.png' : 'file.png');
        $(element).prepend('<img alt="" src="' + src + '" title="' + name + '" /><span>' + name + '</span>');

        var vm = bindingContext['$root'];
        $(element).dblclick(function () {
            if (value.type == 0) { // type: 0-directory, 1-file
                var node = vm.tree().tree('getSelected');
                node.target['__text'] = $.trim($(this).text());
                if (node.state == 'closed') {
                    vm.tree().tree('expand', node.target);
                } else {
                    selectNode(vm.tree(), node);
                }

                vm.fileName('');
            }
        });

        $(element).click(function () {
            $(element).parent().children().each(function () {
                $(this).removeClass('selected');
            });
            $(element).addClass('selected');
            if (value.type == 1) {
                vm.fileName(value.name);
            }
        });
    }
};

ko.bindingHandlers.view = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        $(element).addClass('details');
        viewModel['view']($(element));
    }
};

ko.bindingHandlers.explorer = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        element.vm = viewModel;
    }
};

function getAbsPath(element, node) {
    var path = [$(node.target).text()];
    var parent = null;
    var target = node.target;
    while ((parent = $(element).tree('getParent', target)) != null) {
        path.push($(parent.target).text());
        target = $(element).tree('getNode', parent.target).target;
    }
    return '/cygdrive/' + path.reverse().join('/');
}

function selectNode(element, node) {
    var text = node.target['__text'];
    var children = $(element).tree('getChildren', node.target);
    for (var i in children) {
        if (text == $(children[i].target).text()) {
            $(element).tree('select', children[i].target);
            break;
        }
    }
}

function ExplorerViewModel() {
    var self = this;

    self.roots = ko.observableArray([]);
    self.tree = ko.observable();
    jsonrpc.invoke({
        method: 'files',
        params: {
            path: '/cygdrive',
            type: 0
        },
        success: function (data) {
            self.tree().tree('loadData', data.result);
        }
    });

    self.view = ko.observable();
    self.views = ko.observableArray([{
        id: 0,
        text: 'Details',
        className: 'details'
    }, {
        id: 1,
        text: 'Large icons',
        className: 'large-icons'
    }]);

    self.selectedView = ko.observable();
    self.changeView = function () {
        var views = [];
        for (var i in self.views()) {
            views.push(self.views()[i].className);
        }
        $(self.view()).removeClass(views.join(' '));
        $(self.view()).addClass(self.selectedView().className);
    }

    self.files = ko.observableArray([]);
    self.directories = ko.observableArray([]);

    self.path = ko.observable();
    self.fileName = ko.observable();
}
